using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;

namespace MongoScienceDAL.Core.Tests
{
    [TestClass]
    public class QueryBuilderTests
    {
        [TestMethod]
        public void QueryBuilderNoFiltersShouldReturnNullQueryType()
        {
            var queryCriteria = new QueryCriteria();

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            Assert.AreEqual(resultantQuery, Query.Null);
        }

        [TestMethod]
        public void QueryBuilderTextContains()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextContains("t1", "test");

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var regex = query["t1"].AsRegex;

            const string textString1 = "hamstersAreAwesometestNo";
            const string textString2 = "hamstersaretotalyinatm";

            Assert.IsTrue(regex.IsMatch(textString1));
            Assert.IsFalse(regex.IsMatch(textString2));
        }

        [TestMethod]
        public void QueryBuilderTextStartsWith()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextStartsWith("t1", "test");

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var regex = query["t1"].AsRegex;

            const string textString1 = "testhamstersAreAwesomeNo";
            const string textString2 = "hamstersaretotalyinatm";

            Assert.IsTrue(regex.IsMatch(textString1));
            Assert.IsFalse(regex.IsMatch(textString2));
        }

        [TestMethod]
        public void QueryBuilderTextEndsWith()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextEndsWith("t1", "test");

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var regex = query["t1"].AsRegex;

            const string textString1 = "hamstersAreAwesomeNotest";
            const string textString2 = "hamstersaretotalyinatm";

            Assert.IsTrue(regex.IsMatch(textString1));
            Assert.IsFalse(regex.IsMatch(textString2));
        }

        [TestMethod]
        public void QueryBuilderTextEquals()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextEquals("t1", "test");

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);
            Assert.AreEqual("test", query["t1"]);
        }

        [TestMethod]
        public void QueryBuilderTextRegex()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddTextRegex("t1", "^test");

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var regex = query["t1"].AsRegex;

            Assert.IsNotNull(regex);
            Assert.IsInstanceOfType(regex, typeof(Regex));
        }

        [TestMethod]
        public void QueryBuilderHasField()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddHasField("field1");

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["field1"]);

            var existanceQuery = query["field1"].AsBsonDocument;

            Assert.IsNotNull(existanceQuery["$exists"]);
            Assert.AreEqual(true,existanceQuery["$exists"].AsBoolean);
        }

        [TestMethod]
        public void QueryBuilderDosentHaveField()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDosentHaveField("field1");

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["field1"]);

            var existanceQuery = query["field1"].AsBsonDocument;

            Assert.IsNotNull(existanceQuery["$exists"]);
            Assert.AreEqual(false, existanceQuery["$exists"].AsBoolean);
        }

        [TestMethod]
        public void QueryBuilderDateEquals()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateEquals("t1",DateTime.Today);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);
            Assert.AreEqual(DateTime.Today, query["t1"].AsDateTime.ToLocalTime());
        }

        [TestMethod]
        public void QueryBuilderDateGreaterThan()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateGreaterThan("t1", DateTime.Today);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var subQuery = query["t1"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$gt"]);
            Assert.AreEqual(DateTime.Today, subQuery["$gt"].AsDateTime.ToLocalTime());
        }

        [TestMethod]
        public void QueryBuilderDateGreaterThanOrEqualTo()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateGreaterThanOrEqualTo("t1", DateTime.Today);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var subQuery = query["t1"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$gte"]);
            Assert.AreEqual(DateTime.Today, subQuery["$gte"].AsDateTime.ToLocalTime());
        }

        [TestMethod]
        public void QueryBuilderDateLessThan()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateLessThan("t1", DateTime.Today);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var subQuery = query["t1"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$lt"]);
            Assert.AreEqual(DateTime.Today, subQuery["$lt"].AsDateTime.ToLocalTime());
        }

        [TestMethod]
        public void QueryBuilderDateLessThanOrEqualto()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateLessThanOrEqualTo("t1", DateTime.Today);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var subQuery = query["t1"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$lte"]);
            Assert.AreEqual(DateTime.Today, subQuery["$lte"].AsDateTime.ToLocalTime());
        }

        [TestMethod]
        public void QueryBuilderDateInsideRange()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateInsideRange("t1", DateTime.Today, DateTime.Today.AddDays(5));

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t1"]);

            var subQuery = query["t1"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$gte"]);
            Assert.AreEqual(DateTime.Today, subQuery["$gte"].AsDateTime.ToLocalTime());
            Assert.IsNotNull(subQuery["$lte"]);
            Assert.AreEqual(DateTime.Today.AddDays(5), subQuery["$lte"].AsDateTime.ToLocalTime());
        }

        [TestMethod]
        public void QueryBuilderDateOutsideRange()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddDateOutsideRange("t1", DateTime.Today, DateTime.Today.AddDays(5));

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["$or"]); //this is logical or to get dates outside range
        }

        [TestMethod]
        public void QueryBuilderValueEquals()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueEquals("t2", 5);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t2"]);
            Assert.AreEqual(5, query["t2"].AsDouble);
        }

        [TestMethod]
        public void QueryBuilderValueGreaterThan()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueGreaterThan("t2", 5);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t2"]);

            var subQuery = query["t2"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$gt"]);
            Assert.AreEqual(5, subQuery["$gt"].AsDouble);
        }

        [TestMethod]
        public void QueryBuilderValueGreaterThanOrEqualTo()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueGreaterThanOrEqualTo("t2", 5);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t2"]);

            var subQuery = query["t2"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$gte"]);
            Assert.AreEqual(5, subQuery["$gte"].AsDouble);
        }

        [TestMethod]
        public void QueryBuilderValueLessThan()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueLessThan("t2", 5);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t2"]);

            var subQuery = query["t2"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$lt"]);
            Assert.AreEqual(5, subQuery["$lt"].AsDouble);
        }

        [TestMethod]
        public void QueryBuilderValueLessThanOrEqualTo()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddValueLessThanOrEqualTo("t2", 5);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t2"]);

            var subQuery = query["t2"].AsBsonDocument;

            Assert.IsNotNull(subQuery["$lte"]);
            Assert.AreEqual(5, subQuery["$lte"].AsDouble);
        }

        [TestMethod]
        public void QueryBuilderObjectIdEquals()
        {
            var testId = ObjectId.GenerateNewId();
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddObjectIdEquals(testId);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["_id"]);
            Assert.AreEqual(testId, query["_id"].AsObjectId);
        }

        [TestMethod]
        public void QueryBuilderObjectIdEqualsAny()
        {
            var testIds = new List<ObjectId>
                             {ObjectId.GenerateNewId(), ObjectId.GenerateNewId(), ObjectId.GenerateNewId()};
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddObjectIdEqualsAny(testIds);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["_id"]);
            var subQueryId = query["_id"].AsBsonDocument;

            Assert.IsNotNull(subQueryId["$in"]);
            var subQueryArray = subQueryId["$in"].AsBsonArray;

            Assert.AreEqual(testIds[0], subQueryArray[0].AsObjectId);
            Assert.AreEqual(testIds[1], subQueryArray[1].AsObjectId);
            Assert.AreEqual(testIds[2], subQueryArray[2].AsObjectId);
        }

        [TestMethod]
        public void QueryBuilderBooleanEquals()
        {
            var queryCriteria = new QueryCriteria();
            queryCriteria.AddBooleanEquals("t2", true);

            var resultantQuery = MongoQueryBuilder.Process(queryCriteria);

            var query = (QueryDocument)resultantQuery;

            Assert.IsNotNull(query["t2"]);
            Assert.AreEqual(true, query["t2"].AsBoolean );
        }
    }
}